package net.khoroshev.wschat.service;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.sql.DataSource;

import net.khoroshev.wschat.model.SessionWrapper;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.core.MessageSendingOperations;
import org.springframework.messaging.simp.BrokerAvailabilityEvent;
import org.springframework.stereotype.Service;
import org.springframework.web.context.WebApplicationContext;

@Service
public class SessionService  implements javax.servlet.http.HttpSessionListener, ApplicationContextAware, ApplicationListener<BrokerAvailabilityEvent>{

    private static Log log = LogFactory.getLog(SessionService.class);

    private final MessageSendingOperations<String> messagingTemplate;
    
    private AtomicBoolean brokerAvailable = new AtomicBoolean();
    
    @Autowired
    public SessionService(MessageSendingOperations<String> messagingTemplate) {
        super();
        this.messagingTemplate = messagingTemplate;
    }
    
    /**
     * @return the sessions
     */
    @Bean(name="sessionsMap")
    public Map<String, HttpSession> getSessions() {
        return sessions;
    }

    private Map<String, HttpSession> sessions = new HashMap<String, HttpSession>();
    
    @Autowired
    private DataSource dataSource;
    
    @PostConstruct
    private void postConstruct() {
        log.debug("I'm constructed");
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (applicationContext instanceof WebApplicationContext) {
            ((WebApplicationContext) applicationContext).getServletContext().addListener(this);
        } else {
            //Either throw an exception or fail gracefully, up to you
            throw new RuntimeException("Must be inside a web application context");
        }
    }

    @Override
    public void sessionCreated(HttpSessionEvent se) {
        final String id = se.getSession().getId();
        final SessionWrapper si = new SessionWrapper(se.getSession());
        final String user = si.getUserName();
        log.info("New session: " + id + " : " + si.getUserName());
        sessions.put(id, se.getSession());
        this.messagingTemplate.convertAndSend("/queue/usersOnline", "new session");
        try {
            Connection connection = dataSource.getConnection();
            PreparedStatement st = connection.prepareStatement(
                    "INSERT INTO SESSION (SESS_ID, USER_ID) " +
                    "SELECT ?, USER_ID FROM USERS WHERE USERS.USERNAME = ?");
            st.setString(1, id);
            st.setString(2, user);
            st.execute();
        } catch (SQLException e) {
            log.error(e, e);
        }
        
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
        final String id = event.getSession().getId();
        log.debug("Session destroyed: " + id);
        this.messagingTemplate.convertAndSend("/queue/usersOnline", "session destroyed");
        sessions.remove(id);try {
            Connection connection = dataSource.getConnection();
            PreparedStatement st = connection.prepareStatement("DELETE FROM SESSION WHERE SESS_ID = ?");
            st.setString(1, id);
            st.execute();
        } catch (SQLException e) {
            log.error(e, e);
        }
    }

    @Override
    public void onApplicationEvent(BrokerAvailabilityEvent event) {
        this.brokerAvailable.set(event.isBrokerAvailable());
    }
}
